using System;
using System.Collections.Generic;
using System.IO;
using Framework.Core;
using UnityEditor;
using UnityEngine;

namespace Framework
{
    public interface IGameObjectFieldsEditorProcessor
    {
        void CreateCode(GameObjectFields value);
    }
    
    [CustomEditor(typeof(GameObjectFields), true)]
    public class GameObjectFieldsEditor : Editor
    {
        private static List<string> _componentList = new List<string>();
        private GameObjectFields _target;
        private SerializedProperty _fields;
        private static bool _showAddMenu;
        private static bool _showRemoveMenu;
        private List<IGameObjectFieldsEditorProcessor> _processors;

        protected List<IGameObjectFieldsEditorProcessor> GetProcessor()
        {
            if (_processors == null)
            {
                _processors = new List<IGameObjectFieldsEditorProcessor>();
                var typelist = Utils.Assembly.GetEditorTypes(typeof(IGameObjectFieldsEditorProcessor));
                foreach (var item in typelist)
                {

                    if (System.Activator.CreateInstance(item) is IGameObjectFieldsEditorProcessor cmd)
                    {
                        _processors.Add(cmd);
                    }
                }
                if (_processors.Count > 0)
                {
                    if (_processors.Count > 1)
                    {
                        Debug.LogError("存在多个处理接口,请保留一个");
                    }
                }
            }
            return _processors;
        }
        [InitializeOnLoadMethod]
        static void InitEditor()
        {
            EditorApplication.hierarchyWindowItemOnGUI = onHierarchyWindowItemOnGUI;

        }
        private static void DrawIcon(Rect rect, Texture2D icon)
        {
            if (icon == null)
            {
                return;
            }
            var IconRect = new Rect(rect);
            IconRect.x -= 1;
            // IconRect.height += 3;
            // IconRect.width = IconRect.height;
            // IconRect.y -= 1;
            GUI.Label(IconRect, icon);
        }
        private static void onHierarchyWindowItemOnGUI(int id, Rect selectionRect)
        {
            var config = GameObjectFieldsSetting.Instance;
            if (config != null)
            {
                if (!config.Hint)
                {
                    return;
                }
            }
            var curDrawNode = EditorUtility.InstanceIDToObject(id) as GameObject;
            if (curDrawNode == null)
            {
                return;
            }
            if (Selection.gameObjects.Length > 0)
            {
                var tobj = Selection.gameObjects[0];
                if (tobj == curDrawNode)
                {
                    FieldPopup(selectionRect);
                }
            }

            int findCount = 0;
            var typeName = "";
            var uiForm = GetOwner(curDrawNode);
            if (uiForm != null && uiForm.FieldItems != null && uiForm.FieldItems.Count > 0)
            {
                foreach (var f in uiForm.FieldItems)
                {
                    if (f.Value is Component b)
                    {
                        if (b == null || b.gameObject == null)
                        {
                            continue;
                        }
                        if (b.gameObject == curDrawNode)
                        {
                            findCount++;
                            if (string.IsNullOrEmpty(typeName))
                            {
                                typeName = f.GetShortType();
                            }
                        }
                    }
                    else if (f.Value == curDrawNode)
                    {
                        findCount++;
                        if (string.IsNullOrEmpty(typeName))
                        {
                            typeName = f.GetShortType();
                        }
                    }
                }
            }
            bool isMain = curDrawNode.GetComponent<GameObjectFields>() != null;
            if (isMain)
            {
                if (findCount < 1)
                {

                }
                else
                {

                }
                DrawIcon(selectionRect, config?.GetMainIcon());
            }
            else if (findCount > 0)
            {
                if (findCount > 1)
                {
                    DrawIcon(selectionRect, config?.GetNodeIcon());
                }
                else
                {
                    var icon = config?.GetIcon(typeName);
                    if (icon != null && icon.Icon != null)
                    {
                        DrawIcon(selectionRect, icon.Icon);
                        typeName = null;
                    }
                    else
                    {
                        DrawIcon(selectionRect, config?.GetNodeIcon());
                    }

                }


            }
            if (findCount > 0)
            {
                var showbtn = false;
                if (config != null)
                {
                    showbtn = config.ShowButton;
                }
                var showLabel = false;
                if (config != null)
                {
                    showLabel = config.ShowLabel;
                }
                if (showbtn)
                {
                    //按钮
                    var btnRect = new Rect(selectionRect);
                    btnRect.width = btnRect.height = 16;
                    byte alpha = 0;
                    if (config?.GetNodeIcon() == null)
                    {
                        alpha = 255;
                    }
                    GUI.color = new Color32(159, 233, 250, alpha);
                    if (GUI.Button(btnRect, ""))
                    {
                        showRemoveMenu(curDrawNode, btnRect);
                    }
                    GUI.color = Color.white;
                }

                if (showLabel && !string.IsNullOrEmpty(typeName))
                {
                    //文字
                    GUIStyle labelGUIStyle = new GUIStyle
                    {
                        normal = new GUIStyleState { textColor = new Color32(255, 255, 255, 200) },
                        alignment = TextAnchor.MiddleCenter,
                        fontStyle = findCount > 1 ? FontStyle.Bold : FontStyle.Normal,
                    };
                    if (config != null)
                    {
                        labelGUIStyle.normal.textColor = config.LabelColor;
                    }
                    var labelRect = new Rect(selectionRect);
                    labelRect.width = 16;
                    EditorGUI.LabelField(labelRect, typeName.Substring(0, 1), labelGUIStyle);
                }
            }
            return;

        }
        private static GameObjectFields GetOwner(GameObject selectedObj)
        {
            GameObjectFields parentTool = null;
            Transform curLayer = selectedObj.transform.parent;
            while (curLayer != null && parentTool == null)
            {
                parentTool = curLayer.GetComponent<GameObjectFields>();
                curLayer = curLayer.transform.parent;
            }
            return parentTool;
        }

        private void OnEnable()
        {
            _target = target as GameObjectFields;
            _fields = serializedObject.FindProperty("FieldItems");
        }
        public override void OnInspectorGUI()
        {
            // base.OnInspectorGUI();
            if (EditorApplication.isCompiling || EditorApplication.isUpdating)
            {
                EditorGUILayout.HelpBox("Wiatting for compiling or updating...", MessageType.Warning);
            }
            serializedObject.Update();
            EditorGUILayout.BeginHorizontal();
            EditorGUI.BeginDisabledGroup(EditorApplication.isCompiling || EditorApplication.isUpdating);
            var mButtonstyle = new GUIStyle(GUI.skin.button);
            mButtonstyle.fixedHeight = 30f;
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("检查错误", mButtonstyle))
            {
                _target.Check(true);
                // AssetDatabase.SaveAssetIfDirty(_target);

            }
            if (GUILayout.Button("复制代码", mButtonstyle))
            {
                var processors = GetProcessor();
                if (processors.Count > 0)
                {
                    processors[0].CreateCode(this._target);
                }
                else
                {
                    _target.CopyCode();
                }
            }
            /*
            if (GUILayout.Button("生成C#代码", mButtonstyle))
            {
                var processors = GetProcessor();
                if (processors.Count > 0)
                {
                    processors[0].CreateCode(this._target);
                }
                else
                {
                    EditorUtility.DisplayDialog("提示", "请实现 " + typeof(IGameObjectFieldsEditorProcessor).ToString() + " 接口", "确定");
                }
            }
            */
            EditorGUILayout.EndHorizontal();
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.PropertyField(_fields);

            serializedObject.ApplyModifiedProperties();
        }

        private static List<GameObjectFieldItem> _fieldItems = new List<GameObjectFieldItem>();
        private static Dictionary<string, SelectItem> _fieldMap = new Dictionary<string, SelectItem>();
        private static CustomPool<GameObjectFieldItem> _fieldPool = new CustomPool<GameObjectFieldItem>(() => { return new GameObjectFieldItem(); });
        public static List<GameObjectFieldItem> GetComponents(GameObject value)
        {
            foreach (var item in _fieldItems)
            {
                _fieldPool.Release(item);
            }
            _fieldItems.Clear();
            var components = value.GetComponents<Component>();
            foreach (var item in components)
            {
                var obj = _fieldPool.Get();
                obj.Value = item;
                obj.Type = item.GetType().FullName;
                obj.Name = item.GetType().Name;
                _fieldItems.Add(obj);
            }
            var go = _fieldPool.Get();
            go.Value = value;
            go.Type = value.GetType().FullName;
            go.Name = value.GetType().Name;
            _fieldItems.Add(go);
            return _fieldItems;
        }
        internal class ComponentCount : UnityEngine.Object
        {
            public int Count;
        }
        public static List<GameObjectFieldItem> GetComponents(GameObject[] objs, bool addMode)
        {
            foreach (var item in _fieldItems)
            {
                _fieldPool.Release(item);
            }
            _fieldItems.Clear();
            _fieldMap.Clear();
            foreach (var value in objs)
            {
                var goFields = GetOwner(value);
                if (goFields == null) continue;
                var components = value.GetComponents<Component>();
                var items = new List<SelectItem>();
                foreach (var item in components)
                {
                    var t = item.GetType();
                    if (addMode)
                    {
                        if (goFields.Exist(value, t.FullName))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        if (!goFields.Exist(value, t.FullName))
                        {
                            continue;
                        }
                    }
                    var s = new SelectItem();
                    s.go = value;
                    s.type = t;

                    items.Add(s);
                }
                var got = typeof(GameObject);
                if ((addMode && !goFields.Exist(value, got.FullName)) || (!addMode && goFields.Exist(value, got.FullName)))
                {
                    var s = new SelectItem();
                    s.go = value;
                    s.type = got;
                    items.Add(s);
                }
                foreach (var item in items)
                {
                    if (!_fieldMap.TryGetValue(item.type.FullName, out var sitem))
                    {
                        sitem = new SelectItem();
                        sitem.type = item.type;
                        sitem.count = 0;
                        _fieldMap.Add(item.type.FullName, sitem);
                    }
                    var c = _fieldMap[item.type.FullName];
                    c.count++;
                }

            }
            int count = objs.Length;
            foreach (var item in _fieldMap)
            {
                if (item.Value.count == count)
                {
                    var obj = _fieldPool.Get();
                    obj.Value = null;
                    obj.Type = item.Value.type.FullName;
                    obj.Name = item.Value.type.Name;
                    _fieldItems.Add(obj);
                }
            }
            return _fieldItems;
        }


        [MenuItem("GameObject/添加变量", false, priority = -2)]
        private static void AddFieldPopup()
        {
            _showAddMenu = true;
        }
        [MenuItem("GameObject/添加变量", true)]
        private static bool checkAddFieldPopup()
        {
            if (Selection.gameObjects.Length < 1) return false;
            var strArr = GetComponents(Selection.gameObjects, true);
            if (strArr.Count < 1)
            {
                return false;
            }
            return true;
        }
        [MenuItem("GameObject/移除变量", false, priority = -1)]
        private static void RemoveFieldPopup()
        {
            _showRemoveMenu = true;
        }
        [MenuItem("GameObject/移除变量", true)]
        private static bool checkRemoveFieldPopup()
        {
            if (Selection.gameObjects.Length < 1) return false;
            var strArr = GetComponents(Selection.gameObjects, true);
            if (strArr.Count < 1)
            {
                return false;
            }
            return true;
        }
        private static void FieldPopup(Rect rect)
        {
            if (_showAddMenu)
            {
                _showAddMenu = false;
                if (Selection.gameObjects.Length < 1) return;
                var strArr = GetComponents(Selection.gameObjects, true);
                if (strArr.Count < 1)
                {
                    //EditorUtility.DisplayDialog("提示", "请添加 " + typeof(GameObjectFields).ToString() + "组件", "确定");
                    return;
                }
                int idx = -1;
                var contents = new List<GUIContent>();
                for (int i = 0; i < strArr.Count; i++)
                {
                    contents.Add(new GUIContent("添加 " + strArr[i].Name));
                }
                //rect.width = 200;
                //rect.height = Mathf.Max(100, contents.Count * rect.height);
                EditorUtility.DisplayCustomMenu(rect, contents.ToArray(), idx, (userData, contents, selected) =>
                {
                    var config = strArr[selected];
                    addFileToData(config, selected == contents.Length - 1);
                }, null);
            }

            if (_showRemoveMenu)
            {
                _showRemoveMenu = false;
                if (Selection.gameObjects.Length < 1) return;
                var strArr = GetComponents(Selection.gameObjects, false);
                if (strArr.Count < 1)
                {
                    return;
                }
                int idx = -1;
                var contents = new GUIContent[strArr.Count];
                for (int i = 0; i < strArr.Count; i++)
                {
                    contents[i] = new GUIContent("移除 " + strArr[i].Name);
                }
                //rect.width = 200;
                //rect.height = Mathf.Max(100, contents.Length * rect.height);
                EditorUtility.DisplayCustomMenu(rect, contents, idx, (userData, contents, selected) =>
                {
                    var config = strArr[selected];
                    delFileToData(config);
                }, null);

            }
        }
        private static void showRemoveMenu(GameObject gameObject, Rect rect)
        {
            rect.x += 2;
            var strArr = GetComponents(new GameObject[] { gameObject }, false);
            if (strArr.Count < 1)
            {
                return;
            }
            int idx = -1;
            var contents = new GUIContent[strArr.Count];
            for (int i = 0; i < strArr.Count; i++)
            {
                contents[i] = new GUIContent("移除" + strArr[i].Name);
            }
            EditorUtility.DisplayCustomMenu(rect, contents, idx, (userData, contents, selected) =>
            {
                var config = strArr[selected];
                delOneData(config, gameObject);
            }, null);
        }
        private static void addFileToData(GameObjectFieldItem value, bool isGameObject)
        {
            var objs = Selection.gameObjects;
            foreach (var go in objs)
            {
                var goFields = GetOwner(go);
                if (goFields == null) continue;
                var item = new GameObjectFieldItem();
                item.Name = GameObjectFieldItem.FormatName(go.name);
                item.Type = value.Type;
                if (isGameObject)
                {
                    item.Value = go;
                }
                else
                {
                    var list = GetComponents(go);
                    foreach (var i in list)
                    {
                        if (i.Type == item.Type)
                        {
                            item.Value = i.Value;
                        }
                    }
                }
                goFields.Add(item);
                EditorUtility.SetDirty(goFields);
            }
        }
        private static void delOneData(GameObjectFieldItem value, GameObject target)
        {
            value.Value = target;
            var goFields = value.GetOwner();
            if (goFields == null) return;
            var len = goFields.FieldItems.Count;
            while (len-- > 0)
            {
                var item = goFields.FieldItems[len];
                bool isGameObject = value.Type == typeof(GameObject).FullName;
                if (isGameObject)
                {
                    if (item.Value == target)
                    {
                        goFields.FieldItems.RemoveAt(len);
                        continue;
                    }
                }
                else
                {
                    if (value.Type != item.Type)
                    {
                        continue;
                    }
                    if (item.Value is Component component)
                    {
                        if (component.gameObject != target)
                        {
                            continue;
                        }
                    }
                    var list = GetComponents(target);
                    foreach (var i in list)
                    {
                        if (i.Type == value.Type)
                        {
                            goFields.FieldItems.RemoveAt(len);
                            break;
                        }
                    }
                }
            }
            EditorUtility.SetDirty(goFields);
        }
        private static void delFileToData(GameObjectFieldItem value)
        {
            var objs = Selection.gameObjects;
            foreach (var go in objs)
            {
                var goFields = GetOwner(go);
                if (goFields == null) continue;
                var len = goFields.FieldItems.Count;
                while (len-- > 0)
                {
                    var item = goFields.FieldItems[len];
                    bool isGameObject = value.Type == typeof(GameObject).FullName;
                    if (isGameObject)
                    {
                        if (item.Value == go)
                        {
                            goFields.FieldItems.RemoveAt(len);
                            continue;
                        }
                    }
                    else
                    {
                        if (value.Type != item.Type)
                        {
                            continue;
                        }
                        if (item.Value is Component component)
                        {
                            if (component.gameObject != go)
                            {
                                continue;
                            }
                        }
                        var list = GetComponents(go);
                        foreach (var i in list)
                        {
                            if (i.Type == value.Type)
                            {
                                goFields.FieldItems.RemoveAt(len);
                                break;
                            }
                        }
                    }
                }
                EditorUtility.SetDirty(goFields);
            }
        }
        private class SelectItem
        {
            public GameObject go;
            public Type type;
            public int count;
        }



        [CustomPropertyDrawer(typeof(GameObjectFieldItem))]
        private class FieldItemDrawer : PropertyDrawer
        {
            public string GetShortType(string value)
            {
                string type = value;
                int dotId = type.LastIndexOf(".");
                if (dotId != -1)
                {
                    type = type.Substring(dotId + 1);
                }
                return type;
            }
            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                // base.OnGUI(position, property, label);
                var gap = 8f;
                var w = position.width - gap * 2;
                var xoffset = position.xMin;
                var nameProperty = property.FindPropertyRelative("Name");
                var valueProperty = property.FindPropertyRelative("Value");
                var typeProperty = property.FindPropertyRelative("Type");


                var width = w * 0.2f;
                position.x = xoffset;
                xoffset += width + gap;
                position.width = width;
                EditorGUI.PropertyField(position, valueProperty, GUIContent.none);


                width = w * 0.4f;
                position.x = xoffset;
                xoffset += width + gap;
                position.width = width;
                EditorGUI.PropertyField(position, nameProperty, GUIContent.none);

                // width = w * 0.2f;
                // position.x = xoffset;
                // xoffset += width + gap;
                // position.width = width;
                // EditorGUI.LabelField(position, GetShortType(typeProperty.stringValue));


                width = w * 0.39f;
                position.x = xoffset;
                xoffset += width + gap;
                position.width = width;

                GameObject go = null;
                if (valueProperty.objectReferenceValue is GameObject obj)
                {
                    go = obj;
                }
                else if (valueProperty.objectReferenceValue is Component behaviour)
                {
                    go = behaviour.gameObject;
                }
                if (go != null)
                {
                    var items = GameObjectFieldsEditor.GetComponents(go);
                    if (items.Count > 0)
                    {
                        int len = items.Count;
                        int selected = 0;
                        string[] popupContens = new string[len];
                        for (int j = 0; j < len; j++)
                        {
                            popupContens[j] = items[j].Name;
                            if (items[j].Value == valueProperty.objectReferenceValue)
                            {
                                selected = j;
                            }
                        }
                        EditorGUI.BeginChangeCheck();
                        var selectId = EditorGUI.Popup(position, selected, popupContens);
                        if (EditorGUI.EndChangeCheck())
                        {
                            var fitem = items[selectId];
                            typeProperty.stringValue = fitem.Type;
                            valueProperty.objectReferenceValue = fitem.Value;
                        }
                    }
                }
            }
        }

    }
}